﻿using System;
using System.Collections.Generic;
using System.Linq;
using QQ2564874169.Core.Utils;

namespace QQ2564874169.MessageQueue
{
    public class PublishModelToMq
    {
        public event EventHandler<PublishErrorEventArgs> Error;
        private Func<object, string> _serialize;

        private Queue<object> _cache = new Queue<object>();

        public PublishModelToMq(Func<object, string> serialize)
        {
            _serialize = serialize;

            ExecuteCache();
        }

        private void ExecuteCache()
        {
            TaskHelper.Run(() =>
            {
                while (_cache != null)
                {
                    object[] models;
                    lock (_cache)
                    {
                        models = _cache.ToArray();
                        _cache.Clear();
                    }
                    foreach (var model in models)
                    {
                        ImmediatelyPublish(model);
                    }
                    TaskHelper.Delay(1000).Wait();
                }
            }).ContinueWith(t =>
            {
                ExecuteCache();
            });
        }

        public void ImmediatelyPublish(object model)
        {
            var attrs = model.GetType().GetCustomAttributes<ToMq>(true);

            if (attrs.Any() == false)
            {
                return;
            }

            var msgModel = new ModelMessage
            {
                Id = Guid.NewGuid().ToString(),
                Model = _serialize(model),
                ModelType = model.GetType().AssemblyQualifiedName,
                Time = DateTime.Now.ToLongTime()
            };

            foreach (var attr in attrs)
            {
                msgModel.SQueue = attr.SubscribeQueue;
                var msg = _serialize(msgModel);
                try
                {
                    using (attr.Publisher)
                    {
                        attr.Publisher.Send(attr.PublishQueue, msg);
                    }
                }
                catch (Exception ex)
                {
                    Error?.Invoke(this, new PublishErrorEventArgs(ex, attr, model));
                }
            }
        }

        public void Publish(object model)
        {
            if (model.GetType().GetCustomAttribute<ToMq>(true) == null)
                throw new ArgumentException($"类型{model.GetType().FullName}没有应用{nameof(ToMq)}特性。");

            lock (_cache)
            {
                _cache.Enqueue(model);
            }
        }
    }

    public class PublishErrorEventArgs : EventArgs
    {
        public ToMq Setting { get; private set; }
        public object Model { get; private set; }
        public Exception Error { get; private set; }

        internal PublishErrorEventArgs(Exception ex, ToMq setting, object model)
        {
            Error = ex;
            Setting = setting;
            Model = model;
        }
    }
}
